Approvals V2 — structured grants, counter-proposals, risk & audit A permission ask is no longer Allow/Deny. It is a grant: scope (paths + hosts) × duration (once · this task · this session) × a risk verdict.
Hierarchy law, top to bottom: what it does → why → scope → risk → delta against grants you already gave. The raw command is Details, collapsed — evidence, not the headline.
“Allow for this task” is the recommended default and the only filled button. “Always allow this session” is deliberately demoted into ⋯ More. Timeout always fails closed = denied.
Colour law unchanged: terracotta = the recommended action · amber = medium risk / grant delta · red = high risk, deny, timeout · green at 70% = something that already ran.
Frame 1 · Mobile sheet — normal390×844 · dark · MEDIUM risk · queued 1/3 · details collapsed
9:41
daemon · macbook-pro·claude-sonnet-4.6
CLAUDE
Refactor is in. I'll run the suite and push a coverage report so the PR shows the delta.
edit · src/auth/session.ktdone
bash · ./gradlew assembledone
Run command · BashMedium1/318s
Runs the full test suite and uploads a coverage report to Codecov.
So the pull request for the auth refactor shows the coverage delta.
Scopesrc/**coverage/**api.codecov.io
Sends coverage data to an external host. Reads a token from your environment. No source files are written.
outside current task grant: network access
Details2 lines
Recommended · Allow for this taskMore
Deny Allow once Allow for this task Safer way…
Four asks are never four buttons of equal weight. One filled button = one recommended path; Deny is outlined danger, not filled — denying is cheap and reversible, so it should not shout.
Frame 1b · Details expanded390×844 · the raw command as evidence, h-scrollable
9:41
daemon · macbook-pro·claude-sonnet-4.6
CLAUDE
Refactor is in. I'll run the suite and push a coverage report so the PR shows the delta.
Run command · BashMedium1/313s
Runs the full test suite and uploads a coverage report to Codecov.
So the pull request for the auth refactor shows the coverage delta.
Scopesrc/**coverage/**api.codecov.io
Sends coverage data to an external host. Reads a token from your environment. No source files are written.
outside current task grant: network access
Details2 lines
npm test -- --coverage --runInBand --reporters=default npx codecov -t $CODECOV_TOKEN --slug cc-pocket/app --flags unit
Recommended · Allow for this taskMore
Deny Allow once Allow for this task Safer way…
The code box never wraps — wrapped shell is misread. It scrolls horizontally with a fade edge and a thumb, so a long one-liner can't hide a second command past the fold.
Frame 1c · Timed out390×844 · terminal · fails closed → denied
9:44
daemon · macbook-pro·claude-sonnet-4.6
CLAUDE
Refactor is in. I'll run the suite and push a coverage report so the PR shows the delta.
Run command · BashMedium1/30s
Runs the full test suite and uploads a coverage report to Codecov.
So the pull request for the auth refactor shows the coverage delta.
Scopesrc/**coverage/**api.codecov.io
Sends coverage data to an external host. Reads a token from your environment. No source files are written.
outside current task grant: network access
Details2 lines
Timed out — automatically deniedDismiss
Deny Allow once Allow for this task Safer way…
Timeout is a denial, not a lapse: the banner keeps full contrast while the card behind it drops to 50%, so a glance at a stale phone reads the outcome, not the request. Re-asking is the agent's job.
Frame 2 · “Safer way…” sub-panel390×844 · sheet content transitions in place · 2 constraints selected
9:41
daemon · macbook-pro·claude-sonnet-4.6
CLAUDE
Refactor is in. I'll run the suite and push a coverage report so the PR shows the delta.

Safer way

Medium
Ask the agent to retry with constraints:
No network Read-only Tests only Stay in workspace Patch only (no writes)
write coverage to /tmp
counter-proposal →
retry Bash with: no_network, read_only,
“write coverage to /tmp”
Send to agent Cancel
Counter-proposing beats denying: denial ends the turn, a constraint keeps the work moving. Chips compile to structured flags the daemon enforces — free text rides along as an instruction, never as enforcement.
Frame 3 · Desktop chat stream1200×820 · inline card, HIGH risk, horizontal action row · audit chips grouped and expanded below
cc-pocket
auth-refactor1 permission waiting
docs-passidle · 2h
release-0.9handed off to Mei
auth-refactormacbook-pro · ask modetask grant · 12m left
CLAUDE
Migrations are written. Next I need to apply them against the staging database and drop the deprecated sessions_v1 table.
Run command · BashRisk found2/39s
Applies 3 migrations to the staging database and permanently drops the sessions_v1 table.
To finish the auth refactor by removing the old session store.
Scopedb/migrations/**staging-db.internal:5432DROP TABLE ×1
Destructive and irreversible: 41k rows are deleted with no snapshot in this run. Staging only — production credentials are not in scope.
outside current task grant: database writes, external host
Details4 lines
Deny Allow once Allow for this task Safer way… More
6 actions under task granttask grant9:38–9:40
3 actions under task granttask grant9:41
bash · npm testtask grantView/Tighten9:41:02
edit · src/auth/SessionStore.kttask grantView/Tighten9:41:20
bash · ./gradlew :app:linttask grantHide/Tighten9:41:47
./gradlew :app:lint --offline # exit 0 · 3.4s
Reply to Claude…
⌘↵
Audit chips are log furniture, not messages: no card, no background, mono 12 at secondary weight, green shield at 70%. A run under one grant collapses to a single line so a 40-action task doesn't bury the conversation. Tighten reuses the Safer-way panel to narrow the live grant mid-flight.
Frame 3b · Light themesame card, LOW risk · token swap only
Edit files · WriteLow24s
Rewrites 2 test files to use the new session helper.
The refactor renamed SessionStore.open(), so the old tests no longer compile.
Scopesrc/test/auth/**
Inside the workspace, no network, fully reversible from git.
covered by current task grant
Detailsdiff · +18 −11
DenyAllow onceAllow for this taskSafer way…More
edit · src/App.kttask grantView/Tighten9:42:11
Light theme is a token swap, no layout delta. Only two badges need a light-specific value: LOW loses its dark green-grey and HIGH takes white text on the danger fill to hold contrast.
Frame 4 · Componentsrisk badges · action sets · audit chip anatomy · overflow menu
Risk badge · four states
LowLow — muted grey-green, dot. Reads as “fine”, not as “good”.
MediumMedium — amber #E0A93B, hollow triangle. Same amber as the grant-delta line.
Risk foundHigh — solid #E5604D fill, filled triangle. The only filled badge; also tints the RISK line.
?Not assessedUnknown — hollow + dashed, never red. Absence of a verdict, not a bad verdict.
In context · header row per state
Read files · GlobLow26s
Lists every Kotlin file under src/.
Read-only, inside the workspace.
Run command · BashRisk found7s
Force-pushes the rewritten branch to origin.
Overwrites remote history. Two commits by Mei are not in your local branch.
Run command · MCP?Not assessed21s
Calls deploy.trigger on an unrecognised MCP server.
No verdict — this tool isn't in the risk catalogue.couldn't assess this action reliably
Action set · both layouts
desktop · one row, 30px
DenyAllow onceAllow for this taskSafer way…More
mobile · 2×2, 44pt
DenyAllow onceAllow for this taskSafer way…
⋯ More · overflow
Audit chip · anatomy
bash · npm testtask grantView/Tighten9:41:02
shield-check success @ 70% · sanitized summary mono 12 / secondary · grant-basis pill muted · tail links Inter 11 muted · timestamp mono 10.5, right. Row height 30px — below the 40px tool-event row, so the stream stays legible top-down.
6 actions under task granttask grant9:38–9:40
Consecutive chips under one grant collapse into a group row; the basis pill hoists to the group so the grant is still named while collapsed.
weight ladder, quietest → loudest
audit chip
tool event rowdone
Assistant message